//@author A0105735J
#pragma once
#ifndef RECEIVER_H
#define RECEIVER_H
#include <vector>
#include <algorithm>
#include "Feedback.h"
#include "Storage.h"
#include "Event.h"
#include "DateTime.h"
#include "Logger.h"
#include "SameTypeEvent.h"

const int STATUS_TRANSFORM_PENDING = 3;
const int STATUS_TRANSFORM_DONE = 2;
const int STATUS_TRANSFORM_OVERDUE = 1;

class Receiver
{
private:
	// tempEvents are the temporary storage of calendar
	std::vector<Event*> tempEvents;
	// displayID is the ID for display purpose. It would always starts from 1 and sequentially distributed
	int displayID;
	// searchID is the ID to facilitate execution within Receiver. It is the unique ID for each event
	int searchID;
	Storage* storage;

public:
	Receiver(void);
	// This constructor is for unit test to use 
	Receiver(Storage*);
	~Receiver(void);

	// Return the feedback for an invalid command
	// The feedback contains failure message
	// This function is used when there is no invalid command to be executed
	Feedback invalidCommand(Error);

	// Add an event pointer to the temporary calendar in Receiver
	// During the add process, the event display ID will be set automatically
	// Also write the temporary calendar into Storage
	// Return the feedback for successfully adding an event
	// This function is used when the command is to add a new event into calendar
	Feedback addCommand(Event*);

	// input vector<int> ID is the vector of integers, which is the display ID for events
	// Delete pointers of events corresponding to ID in the vector in the temporary calendar
	// If at least one display ID is not found, delete the rest and return the feedback contains not found ID message
	// If all desired pointers of events are being deleted, return the feedback contains successfully delete message
	// During the delete process, the event display ID will be set automatically
	// Also write the temporary calendar into Storage
	// Return the feedback for successfully deleting events
	// This function is used when the command is to delete events by their display ID 
	Feedback deleteCommand(std::vector<int>);

	// The input new event contains the new message to be updated and the display ID of the target event
	// Generate a new event contains the new information
	// Delete the old event
	// Add the new event
	// During the update process, the event display ID will be set automatically
	// Also write the temporary calendar into Storage
	// Return the feedback for successfully updating an event
	// This function is used when the command is to update an event by its display ID
	Feedback updateCommand(int, Event*);

	// The input are two event's pointer, one is old event to be updated, one is the new event containing new information
	// Generate a output event pointer with the desired type
	// Copy information from old event into the output event
	// Replace the old information in the output event by the new information in the new event
	// This function is used by updateCommand function to get the output Event pointer
	Event* updateTargetEvent(Event*, Event*);

	// The input are two event's pointer, one is old event to be updated, one is the new event containing new information
	// By comparing the event type in the two event, creating a new pointer with the desired event type and return it
	// This function is to return a pointer with desired event type.
	Event* updateEventType(Event*, Event*);

	// The input are two event's pointer, one is output event to be returned, one is old event to be updated
	// Generate a output event pointer with the desired type
	// This function is to copy information
	void copyEventInfo(Event*, Event*);

	// The input are vector of display ID, which corresponds to events to be marked, and a string which is the status type
	// Mark pointers of events corresponding to display ID as done in the vector in the temporary calendar
	// If at least one displayID is not found, mark the rest and return the feedback contains not found ID message
	// If all desired pointers of events are being marked as done, return the feedback contains successfully mark message
	// During the delete process, the event display ID will be set automatically
	// Also write the temporary calendar into Storage
	// Return the feedback for successfully marking events and with the events being marked in the feedback
	// Usually the input string is "done", but during undo process, the string is the original status
	// This function is used when the command is to mark events by their display ID
	Feedback markCommand(std::vector<int>, std::string);

	// No input for this function
	// Get the current time first
	// Compare all the events in the calendar's end time with the current time
	// if the current time is before the event's end time, mark the event as overdue
	// Also write the temporary calendar into Storage
	// Return the feedback for successfully checking events status and with the events being checked in the feedback
	// This function is used when the command is to check the overdue events
	Feedback checkCommand();

	// No input for this function
	// Return the current time
	DateTime getCurrentTime();

	// The input is the string of the keyword to be searched
	// Search all the events in the calendar, compare whether there is any match in name, location and importance level
	// If the target is found, then write all the result events into output as Feedback, else write error of not found message in Feedback
	// Return the feedback for successfully found some result and the targeted events in the feedback or unsuccessfully not found
	// This function is used when the command is to search the keyword
	Feedback searchCommand(std::string);

	// The input of this function is two DateTime, one is start time one is end time
	// Check all the events in the temporary calendar, to see wester a event is between the start time and end time
	// Only year, month and day will be checked
	// Return the feedback for successfully displaying events and write the targeted events in the feedback
	// This function is used when the command is to display the events in the time duration
	Feedback displayCommand(DateTime, DateTime);	

	// This function is to compare two DateTime object, to indicate whether the first DateTime is equal the Second DateTime
	// In this function, only the Year, Month, Day will be compared if necessary
	bool equalDate(DateTime, DateTime);
	
	// This function is to compare two DateTime object, to indicate whether the first DateTime is before the Second DateTime
	// In this function, only the Year, Month, Day will be compared if necessary
	bool beforeDate(DateTime, DateTime);

	// This function is to compare two DateTime object, to indicate whether the first DateTime is before the Second DateTime
	// In this function, all the Year, Month, Day, Hour and MIn will be compared if necessary
	bool beforeTime(DateTime, DateTime);

	// Clear all the event's display ID (set them to be 0)
	void clearID();

	// This function is get a pointer points to the desired event with the target ID
	// The two input is ID and mode
	// if it is mode 1 (mode == 1), then it return  a pointer points to the desired event with the target displauyID
	// if it is mode 2 (mode == 2), then it return  a pointer points to the desired event with the target searchID
	// Mode 2 is used when the display ID of a event is changed, then use search ID to find the event
	// This function is being called when other function want to find the pointer points to the desired event with the target ID
	Event* getTargetEvent(int, int);

	// The input of this function is one vector of pointers of Events to be set display ID and the number of the first display ID to be set
	// In this function, events in the vector will be automatically sorted
	// First category is TimedEvent, which is sorted by start time
	// Second category is DeadlineEvent, which is sorted by end time
	// Third category is Floating Event, which is sorted by event status
	// This function is called when the temporary calendar is changed
	void setEventsID(std::vector<Event*>&, int);
	
	// initialize the tempEvents by calling readFile function in storage
	// return the Feedback of successfully read the file and the events from the local calendar
	Feedback readFileCommand();

	// save the tempEvents by calling writeFile function in storage
	void writeFile();

	// Generate a Feedback object with the input of vector of event pointers and the Succeed type
	// This function is called when the command is successfully executed and need to return a feedback
	Feedback writeSucceedFeedback(std::vector<Event*>, Succed);

	// Generate a Feedback object with the input Error type
	// This function is called when the command is unsuccessfully executed and need to return a feedback
	Feedback writefailureFeedback(Error);

	// the following functions define the way the events is being sorted

	// This function is to rank event by start time, only for TimedEvent
	static bool rankByStartTime(TimedEvent * a, TimedEvent * b){
		DateTime temp1, temp2;
		temp1 = a->getTimedEventStartTime();
		temp2 = b->getTimedEventStartTime();
		return compareDateTime(temp1,temp2);
	}

	// This function is to rank event by end time, only for DeadlineEvent
	static bool rankByEndTime(DeadlineEvent * a, DeadlineEvent * b){
		DateTime temp1, temp2;
		temp1 = a->getDeadlineEventEndTime();
		temp2 = b->getDeadlineEventEndTime();
		return compareDateTime(temp1,temp2);		
	}

	// This function is to rank event by status
	static bool rankByStatus(Event * a, Event * b){
		// Pending > done > overdue
		std::string s1, s2;
		s1 = a->getEventStatus();
		s2 = b->getEventStatus();
		int i1 = transformStatus(s1);
		int i2 = transformStatus(s2);
		if(i1 > i2){
			return true;
		}else{
			return false;
		}
	}

	// This function is to rank event by ID
	static bool rankByID(Event * a, Event * b){
		if(a->getDisplayID() > b->getDisplayID()){   
			return false;
		}else{
			return true;
		}
	}

	// This function is to compare whether the time temp1 is before the time temp2
	static bool compareDateTime(DateTime temp1, DateTime temp2){
		if(temp1.getYear() > temp2.getYear()){   
			return false;
		}else if(temp1.getYear() < temp2.getYear()){   
			return true;
		}
		else if(temp1.getMonth() > temp2.getMonth()){
			return false;
		}
		else if(temp1.getMonth() < temp2.getMonth()){
			return true;
		}
		else if(temp1.getDay() > temp2.getDay()){
			return false;
		}
		else if(temp1.getDay() < temp2.getDay()){
			return true;
		}
		else if(temp1.getHour() > temp2.getHour()){
			return false;
		}
		else if(temp1.getHour() < temp2.getHour()){
			return true;
		}
		else if(temp1.getHour() == temp2.getHour()){
			return false;
		}
		else{	
			return true;
		}		
	}

	// This function is to transform status to integer for comparing purpose
	static int transformStatus(std::string s){
		if(s == PENDING){
			return STATUS_TRANSFORM_PENDING;
		}else if(s == DONE){
			return STATUS_TRANSFORM_DONE;
		}else{
			return STATUS_TRANSFORM_OVERDUE;
		}
	}

	// This function is used by unit test only
	// This function returns the temporary calendar in the Receiver
	std::vector<Event*> getTempEvents();

};
#endif